package org.totoro.memory.ref;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.List;

/**
 * 虚引用，如果有对象A引用了虚引用的对象B，只要A 没有被垃圾回收，B也不会进行垃圾回收，所以虚引用类型，一般可以用来监控对象被垃圾回收后，进行一些操作 例如：sun.misc.Cleaner
 * <p>
 * <p>
 * 四种不同的状态
 * Active，这是Reference对象初创的时候的状态
 * Pending，对象在pending列表里，等待进入队列。没有注册队列的引用对象不存在这个状态
 * Enqueued，引用对象进入队列，没有注册队列的引用对象不存在这个状态
 * Inactive，没有注册队列的引用对象或者从队列中被移除之后所处的状态，这个状态下的对象不再变化，等待gc回收。
 * <p>
 * 回收时机：PhantomReference的get方法总是返回null，因此无法访问对应的引用对象；其意义在于说明一个对象已经进入finalization阶段，可以被gc回收，用来实现比finalization机制更灵活的回收操作。
 *
 * <p>
 * <p>
 * vm:-Xmx20m -verbose:gc -XX:+PrintGCDetails
 *
 * @author daocr
 * @date 2020/3/7
 */
public class PhantomReferenceTest {

    public static final int _2MB = 1024 * 1024 * 2;

    public static void main(String[] args) throws InterruptedException {
        // 引用信息放到队列中
        ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();

        // 引用链路  ：  list 强引用 -> phantomReference 虚引用 -> byte 数组，这个list 没有被垃圾回收 则虚引用类型的对象也不会进行垃圾回收，所以会导致 oom
        List<PhantomReference<byte[]>> phantomReferences = new ArrayList<>();

        for (int i = 1; i <= 10; i++) {
            byte[] bytes = new byte[_2MB];
            PhantomReference<byte[]> phantomReference = new PhantomReference<>(bytes, referenceQueue);

            // 注释掉，就可以不oom
//            phantomReferences.add(phantomReference);
            System.out.println("第" + i + "申请 2mb 内存" + bytes);

            for (PhantomReference<byte[]> item : phantomReferences) {
                System.out.print("回收：" + item.isEnqueued() + "\t");
            }

            System.out.println();

            Thread.sleep(1000);
        }


        Reference<? extends byte[]> poll = referenceQueue.poll();

        while (poll != null) {
            System.out.println(poll);
            referenceQueue.poll();
        }


    }
}
